﻿//-----------------------------------------------------------------------
// <copyright file="MessageFormatter.cs" company="AFS">
// Copyright (c) AFS. All rights reserved.
//
//    This file is part of Chatbox.
//
//    Chatbox is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Lesser General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    any later version.
//
//    Chatbox is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with Chatbox. If not, see http://www.gnu.org/copyleft/lesser.html
// </copyright>
//----------------------------------------------------------------------- 

namespace ChatBox.UI.MessageDisplay
{
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Documents;
    using System.Windows.Media;

    using ChatBox.DomainObjects.MessageTypes;
    using ChatBox.DomainObjects.XmlFeedObjects;
    using ChatBox.XamlUtility;

    using Interfaces;

    /// <summary>
    /// Format the message using the appropriate formatter
    /// </summary>
    public class MessageFormatter
    {
        #region data 

        /// <summary>
        /// Map message type to formatter
        /// </summary>
        private readonly Dictionary<MessageType, IMessageFormatter> map = new Dictionary<MessageType, IMessageFormatter>();

        /// <summary>
        /// The message handler to attach to link clicks
        /// </summary>
        private readonly HyperlinkContext hyperlinkContext;

        /// <summary>
        /// The error logger
        /// </summary>
        private readonly ILogger logger;

        #endregion

        #region constructor

        /// <summary>
        /// Initializes a new instance of the MessageFormatter class
        /// </summary>
        /// <param name="hyperlinkContext">the handlers for when a link is clicked on</param>
        /// <param name="logger">the error logger</param>
        public MessageFormatter(HyperlinkContext hyperlinkContext, ILogger logger)
        {
            this.hyperlinkContext = hyperlinkContext;
            this.logger = logger;

            this.InitialiseMap();
        }

        #endregion

        #region public

        /// <summary>
        /// Gets the Hyperlink context
        /// </summary>
        public HyperlinkContext HyperlinkContext
        {
            get
            {
                return this.hyperlinkContext;
            }
        }

        /// <summary>
        /// Generate a flow document from a list of messages
        /// </summary>
        /// <param name="messages">the messages to read</param>
        /// <param name="formatOptions">formatting options</param>
        /// <returns>the generated document</returns>
        public FlowDocument GenerateMessagesDocument(IEnumerable<Message> messages, FormatOptions formatOptions)
        {
            FlowDocument resultDocument = new FlowDocument();
            resultDocument.PagePadding = new Thickness(2);
            Paragraph contentPara = new Paragraph();
            contentPara.FontSize = formatOptions.FontSize;

            bool hasItems = false;
            FormatOptions privateMessageFormatOptions = new FormatOptions(formatOptions);
            privateMessageFormatOptions.ShowDate = DateTimeDisplay.None;
            privateMessageFormatOptions.MarkPrivateMessages = true;

            foreach (var message in messages)
            {
                if (hasItems)
                {
                    // put a linebreak after all items except the last 
                    // (i.e before all items except the first)
                    contentPara.Inlines.Add(new LineBreak());
                }

                if (formatOptions.MarkPrivateMessages && !message.IsPublic)
                {
                    Brush foreGround = Application.Current.Resources["PrivateMessageInPublic"] as Brush;
                    Inline dateRun = MessageDisplayHelpers.DateFormat(message, formatOptions.ShowDate);
                    dateRun.Foreground = foreGround;
                    contentPara.Inlines.Add(dateRun);

                    Run fromRun = new Run(" Private message from ");
                    fromRun.Foreground = foreGround;
                    contentPara.Inlines.Add(fromRun);
                    this.FormatMessageIntoParagraph(message, contentPara, privateMessageFormatOptions);
                }
                else
                {
                    this.FormatMessageIntoParagraph(message, contentPara, formatOptions);
                }

                hasItems = true;
            }

            if (hasItems)
            {
                resultDocument.Blocks.Add(contentPara);
            }
            else
            {
                Italic noMessagesText = new Italic(new Run("No Messages"));
                resultDocument.Blocks.Add(new Paragraph(noMessagesText));
            }

            return resultDocument;
        }

        #endregion

        #region private

        /// <summary>
        /// Format the message for display in the paragraph
        /// </summary>
        /// <param name="message">the message to display</param>
        /// <param name="paragraph">the paragraph to recieve markup</param>
        /// <param name="formatOptions">formatting options</param>
        private void FormatMessageIntoParagraph(Message message, Paragraph paragraph, FormatOptions formatOptions)
        {
            // find a matching formatter
            IMessageFormatter formatter = this.map[message.MessageType];
            if (formatter == null)
            {
                // may be an error if no formatter was found. Apply the default.
                formatter = this.map[MessageType.Plaintext];
            }

            // apply the fomatter
            formatter.FormatMessageIntoParagraph(message, paragraph, formatOptions);
        }

        /// <summary>
        /// Initilaise the map of formatters for message types
        /// </summary>
        private void InitialiseMap()
        {
            this.AddMapping(MessageType.Plaintext, new PlainMessageFormatter());
            this.AddMapping(MessageType.Me, new ItalicMessageFormatter());
            this.AddMapping(MessageType.Whisper, new SmallMessageFormatter());
            this.AddMapping(MessageType.Sing, new SingMessageFormatter());
            this.AddMapping(MessageType.Egg, new SmallCapsMessageFormatter(" EGGS"));
            this.AddMapping(MessageType.Smite, new SmallCapsMessageFormatter(" SMITES"));
            this.AddMapping(MessageType.Rolls, new SmallCapsMessageFormatter(" ROLLS"));
        }

        /// <summary>
        /// Add a mapping
        /// </summary>
        /// <param name="messageType">the messag type to map from</param>
        /// <param name="messageFormatter">the formatter to configure and store</param>
        private void AddMapping(MessageType messageType, IMessageFormatter messageFormatter)
        {
            messageFormatter.HyperlinkContext = this.hyperlinkContext;
            messageFormatter.Logger = this.logger;

            this.map.Add(messageType, messageFormatter);
        }

        #endregion
    }
}
